﻿using System;
using System.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace WindowsAPI.PE
{
    /// <summary>Класс, реализующий взаимодействие с динамически подключаемыми библиотеками DLL</summary>
    public class Library : IDisposable
    {
        /* ------------------------------------------------------------------------------------------ */

        /// <summary>Признак освобождения ресурсов</summary>
        private bool f_Disposed;

        /// <summary>Информация о файле библиотеки</summary>
        private readonly FileInfo f_File;

        /// <summary>Дескриптор модуля в памяти</summary>
        private readonly IntPtr f_Handle;

        /* ------------------------------------------------------------------------------------------ */

        /// <summary>Новый экземпляр обёртки библиотеки PE-файла</summary>
        /// <param name="file">Файл библиотеки</param>
        /// <exception cref="FileNotFoundException">Возникает, если файл отсутствует</exception>
        public Library(FileInfo file)
        {
            if(!file.Exists) throw new FileNotFoundException("Не найден файл библиотеки " + file);
            f_File = file;
            f_Handle = Kernel32.LoadLibrary(file.FullName);
            if(f_Handle == IntPtr.Zero)
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        ~Library() { Dispose(); }

        /* ------------------------------------------------------------------------------------------ */

        /// <summary>Получить экспортируемый библиотекой метод</summary>
        /// <typeparam name="TDelegate">Тип делегата</typeparam>
        /// <param name="Name">Имя функции в таблице экспорта библиотеки</param>
        /// <returns>Делегат, инкапсулирующий вызов экспортируемой библиотекой функции</returns>
        public Delegate GetMethod<TDelegate>(string Name)
        {
            var lv_MethodHandle = Kernel32.GetProcAddress(f_Handle, Name);
            if(lv_MethodHandle == IntPtr.Zero)
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            return Marshal.GetDelegateForFunctionPointer(lv_MethodHandle, typeof(TDelegate));
        }

        /// <summary>Получить экспортируемый ресурс-значёк библиотеки</summary>
        /// <param name="Index">Индекс</param>
        /// <returns>Экспортируемый библиотекой значёк</returns>
        public Icon GetIcon(int Index = 0)
        {
            var handle = Shell32.ExtractIcon(IntPtr.Zero, f_File.FullName, Index);
            var ico = Icon.FromHandle(handle);
            User32.DestroyIcon(handle);
            return ico;
        }


        /* ------------------------------------------------------------------------------------------ */

        #region Implementation of IDisposable

        /// <summary>
        /// Выполняет определяемые приложением задачи, связанные с удалением, высвобождением или сбросом неуправляемых ресурсов.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Dispose()
        {
            if(f_Disposed) return;
            var lv_ErrorCode = 0;
            var error = !Kernel32.FreeLibrary(f_Handle);
            if(error) lv_ErrorCode = Marshal.GetHRForLastWin32Error();
            f_Disposed = true;
            GC.SuppressFinalize(this);
            if(error) Marshal.ThrowExceptionForHR(lv_ErrorCode);
        }

        #endregion

        /* ------------------------------------------------------------------------------------------ */
    }
}
